package com.zz.common.tools.box.tools;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URI;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HttpContext;

public class BpClientUrlLoader extends BpFuture implements RedirectHandler {
	
	public interface OnErrorListener {
		public void onError(int errCode, BpClientUrlLoader loader);
	}
	
	public interface OnReadListener {
		public void onRead(byte[] buffer, int readedBytes, BpClientUrlLoader loader);
	}
	
	public interface OnResponseListener {
		public void onResponse(int respCode, Header[] headers, BpClientUrlLoader loader);
	}
	
	public interface OnCompletedListener	{
		public void onComplete(int respCode, Header[] headers, 
						byte[] content, BpClientUrlLoader loader);
	}
	
	private static final String TAG = "BpClientUrlLoader";
	private static final int DEFAULT_CONNECT_TIME_OUT = 60000;
	private static final int DEFAULT_READ_TIME_OUT = 300000;
	
	private OnErrorListener mOnErrorListener;
	private OnCompletedListener mOnCompletedListener;
	private OnReadListener mOnReadListener;
	private OnResponseListener mOnResponseListener;
	private RedirectHandler mRedirectHandler;
	
	private String mUrl;
	private Header[] mHeaders;
	private int mConnectionTimeOut;
	private int mReadTimeOut;
	private String mUserAgent;
	
	private boolean mCancel = false;
	
	private Thread mThread = null;
	
	public BpClientUrlLoader(String url) {
		mUrl = url;
		mConnectionTimeOut = DEFAULT_CONNECT_TIME_OUT;
		mReadTimeOut = DEFAULT_READ_TIME_OUT;
		mCancel = false;
	}
	
	public String getUrl() {
		return mUrl;
	}
	
	public void setOnErrorListener(OnErrorListener listener)	{
		if(null != listener) {
			mOnErrorListener = listener;
		}
	}
	
	public void setOnCompleteListener(OnCompletedListener listener) {
		if(null != listener) {
			mOnCompletedListener = listener;
		}
	}
	
	public void setmOnReadListener(OnReadListener listener) {
		if(null != listener) {
			mOnReadListener = listener;
		}
	}
	
	public void setOnResponseListener(OnResponseListener l) {
		mOnResponseListener = l;
	}
	
	public void setRedirectHandler(RedirectHandler h) {
		mRedirectHandler = h;
	}
	
	public void setTimeOut(int connectionTime, int readTime) {
		if(connectionTime > 0) {
			mConnectionTimeOut = connectionTime;
		}
		if(readTime > 0) {
			mReadTimeOut = readTime;
		}
	}
	
	public void setUserAgent(String userAgent) {
		mUserAgent = userAgent;
	}
	
	public String getHeader(String name) {
		if(null == mHeaders || null == name) {
			return null;
		}
		for(int i=0; i<mHeaders.length; i++) {
			if(name.equalsIgnoreCase(mHeaders[i].getName())) {
				return mHeaders[i].getValue();
			}
		}
		
		return null;
	}

	@Override
	public void run() {
		try	{
			mThread = Thread.currentThread();
			sailing();
		} catch (InterruptedException e) {
		}
	}
	
	@Override
	public void cancel() {
		super.cancel();
		mOnReadListener = null;
		mOnResponseListener = null;
		mOnErrorListener = null;
		mOnCompletedListener = null;
		if(null != mThread && !mThread.isInterrupted())	{
			mThread.interrupt();
			mThread = null;
		}
	}
	
	@Override
	public URI getLocationURI(HttpResponse response, HttpContext context)
			throws ProtocolException {
		Header[] newUrlList = response.getHeaders("Location");
		if(null == newUrlList || newUrlList.length == 0) {
			newUrlList = response.getHeaders("location");
		}
		if(null == newUrlList || newUrlList.length == 0) {
			return null;
		} else {
			String newUrl = newUrlList[0].getValue();
			return URI.create(newUrl);
		}
	}

	@Override
	public boolean isRedirectRequested(HttpResponse response,
			HttpContext context) {
		int statusCode = response.getStatusLine().getStatusCode();
	    switch (statusCode) {
		    case HttpStatus.SC_MOVED_TEMPORARILY:
		    case HttpStatus.SC_MOVED_PERMANENTLY:
		    case HttpStatus.SC_SEE_OTHER:
		    case HttpStatus.SC_TEMPORARY_REDIRECT:
		    	return true;
		    	
		    default:
		    	break;
	    }

		return false;
	}
	
	private void sailing() throws InterruptedException {
		if(mCancel)	{
			return;
		}
		
		InputStream is = null;
		ByteArrayOutputStream os = null;
		try {
	    	DefaultHttpClient client = new DefaultHttpClient();
	    	HttpConnectionParams.setConnectionTimeout(client.getParams(), mConnectionTimeOut);
	    	HttpConnectionParams.setSoTimeout(client.getParams(), mReadTimeOut);
	    	if(null != mRedirectHandler) {
	    		client.setRedirectHandler(mRedirectHandler);	
	    	}
	    	HttpGet getter = new HttpGet(mUrl);
	    	if(null != mUserAgent) {
	    		getter.setHeader("User-Agent", mUserAgent);
	    	}
	    	HttpResponse response = client.execute(getter);
	    	if(Thread.interrupted()) {
	    		return;
	    	}
	    	int status = response.getStatusLine().getStatusCode();
	    	mHeaders = response.getAllHeaders();
	    	if(null != mOnResponseListener) {
	    		mOnResponseListener.onResponse(status, mHeaders, this);
	    	}
	    	if(status != HttpStatus.SC_OK) {
	    		if(null != mOnErrorListener) {
	    			mOnErrorListener.onError(-1, this);
	    		}
			}
	    	if(status == HttpStatus.SC_OK && !Thread.interrupted()) {
	    		HttpEntity entity = response.getEntity();
	    		is = entity.getContent();
	    		os = new ByteArrayOutputStream();
	    		byte[] buffer = new byte[16*1024];
	    		int len = 0;
	    		if(!Thread.interrupted()) {
	    			len = is.read(buffer);
	    			while(-1 != len && !Thread.interrupted()) {
	    				os.write(buffer, 0, len);
	    				len = is.read(buffer);
	    				if(null != mOnReadListener) {
	    					mOnReadListener.onRead(buffer, len, this);
	    				}
	    			}
	    		}
	    		if(null != mOnCompletedListener) {
	    			mOnCompletedListener.onComplete(status, mHeaders, os.toByteArray(), this);
	    		}
 	    	}    	
	    	
		} catch(Exception e) {
			if(null != mOnErrorListener) {
				mOnErrorListener.onError(-1, this);
			}
		} finally {
			if(null != is) {
				try {
					is.close();
				} catch(Exception ex) {
				}
			}
			if(null != os) {
				try {
					os.close();
				} catch(Exception ex) {
				}
			}
		}
	}

}
